#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using IPSim.Data;
using IPSim.Util;

namespace IPSim.Core
{

    public class TransportAppDataEventArgs : EventArgs
    {
        private readonly IAppData m_appData;

        /// <summary>
        /// 
        /// </summary>
        public TransportAppDataEventArgs(IAppData appData)
        {
            m_appData = appData;
        }

        /// <summary>
        /// Data encapsulated in event argument
        /// </summary>
        public IAppData AppData
        {
            get { return m_appData; }
        }

    }
    
    public delegate void TransportAppDataEventHandler(object source, TransportAppDataEventArgs e);

    public class UDPInterface
    {
        /// <summary>
        /// Buffer of UDPPackets recieved from IP Interface
        /// </summary>
        private Queue<IPSim.Data.UDPPacket> m_udpBuffer;
        /// <summary>
        /// Thread collecting packets from buffer and calling process method for them.
        /// Consists of PacketProcessorMethod()
        /// </summary>
        private Thread m_packetProcessorThread;
        /// <summary>
        /// IPInterface below UDPInterface
        /// </summary>
        private IPInterface m_ipInterface;

        public event TransportAppDataEventHandler TransportAppData;

        /// <summary>
        /// Thread method collecting packets from buffer and calling process method for them.
        /// </summary>
        private void PacketProcessorMethod()
        {
            UDPPacket tempPacket;
            while (true)
            {
                lock (m_udpBuffer)
                {
                    tempPacket = null;
                    while (m_udpBuffer.Count == 0) Monitor.Wait(m_udpBuffer);
                    tempPacket = m_udpBuffer.Dequeue();
                }
                ProcessPacket(tempPacket);
            }
        }

        /// <summary>
        /// Method that processes UDP packet received from IP interface
        /// </summary>
        /// <param name="udpPacket"></param>
        private void ProcessPacket(UDPPacket udpPacket)
        {
            Logger.log("[" + m_ipInterface.IPAddress + "] UDP packet received", LogLevel.INFO);
            udpPacket.Data.SenderIPAddress = udpPacket.SenderIPAddress;
            if (udpPacket.Data is RIPPacket)
                Logger.log("received UDP packet contains RIP packed", LogLevel.INFO);
            if(TransportAppData!=null)
                TransportAppData(this, new TransportAppDataEventArgs(udpPacket.Data));
        }

        /// <summary>
        /// Method called by event when IP Interface have some packet to send.
        /// Checks if packet is UDP type, then put it in buffer.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void ReceiveIPData(object source, TransportIPDataEventArgs e)
        {
            UDPPacket tempPacket;
            if (e.IPData.DeclaringType == typeof(UDPPacket))
            {
                tempPacket = e.IPData as UDPPacket;
                lock (m_udpBuffer)
                {
                        m_udpBuffer.Enqueue(tempPacket);
                        Monitor.PulseAll(m_udpBuffer);
                }                
            }
        }

        /// <summary>
        /// Sends UDP Packet to IP Interface
        /// </summary>
        /// <param name="udpPacket"></param>
        /// <param name="destIP"></param>
        public void SendUDPPacket(UDPPacket udpPacket, IPAddress destIP)
        {
            Logger.log("[" + m_ipInterface.IPAddress + "] Sending UDP packet", LogLevel.INFO);
            if(udpPacket.Data is RIPPacket)
                m_ipInterface.SendEthernetData(udpPacket, destIP, IPPacket.IPProtocol.UDP,1);
            else
                m_ipInterface.SendEthernetData(udpPacket, destIP, IPPacket.IPProtocol.UDP);
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="ipInterface"></param>
        public UDPInterface(IPInterface ipInterface)
        {
            m_udpBuffer = new Queue<UDPPacket>();
            m_ipInterface = ipInterface;
            m_ipInterface.TransportIPData += new TransportIPDataEventHandler(this.ReceiveIPData);

            m_packetProcessorThread = new Thread(new ThreadStart(PacketProcessorMethod));
            m_packetProcessorThread.Start();
            Logger.log("[" + m_ipInterface.IPAddress + "] UDP Interface CREATED!", LogLevel.INFO);
        }

        ~UDPInterface()
        {
            Logger.log("UDP Interface stoping", LogLevel.INFO);
            if (m_packetProcessorThread != null)
            {
                m_packetProcessorThread.Abort();
            }
        }

    }
}
